﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using dotNails.CodeGen.Models;
using Microsoft.VisualStudio.TextTemplating.VSHost;
using System.IO;
using Microsoft.VisualStudio.Shell;
using EnvDTE;
using System.Xml.Linq;
using dotNails.Util;
using dotNails.CodeGen.Util;
using System.Reflection;

namespace dotNails.UI {
	/// <summary>
	/// Interaction logic for DatabaseList.xaml
	/// </summary>
	public partial class DatabaseList : UserControl, IChildUserControl {
		private List<DbmlData> _dbmls;
		private IUserControlParent _parent;
		private DbmlData _selectedDbml;

		public DatabaseList(IUserControlParent parent, List<DbmlData> dbmls) {
			InitializeComponent(); 
			_dbmls = dbmls;
			_parent = parent;
		}

		public void Display() {
			Databases.ItemsSource = _dbmls;

			// TODO: Add option to create dbml/database
			//  Allow user to select project, then folder within project - OR require the user to right click in the folder where they want to create a database and
			//  setup the addIn to provide that option only on a folder or project (not the solution) - or do both

			// TODO: Hide buttons on each dbml as necessary
			//  Sync and remove should only show up if there is at least one table in the dbml
			//  Add should only show up if there is a connection string
			//  If there is no connection string, the only button should be to establish a connection string (make a new button)
		}

		//private void Databases_SelectionChanged(object sender, SelectionChangedEventArgs e) {
		//    DbmlData dbml = Databases.SelectedItem as DbmlData;

		//    if (dbml != null) {
		//        if (dbml.DB != null && dbml.DB.Connection != null && !string.IsNullOrEmpty(dbml.DB.Connection.ConnectionString)) {
		//            // If we have a connection string, go straight to the DB Manager user control
		//            UserControl nextUserControl = new DBManager(_parent, dbml);
		//            _parent.LoadUserControl(nextUserControl);
		//        }
		//        else {
		//            // No connection string means we need to define one - popup the ConnectionStringManager dialog
		//            ConnectionStringDialog.DbmlData = dbml;
		//            ConnectionStringDialog.ConnectionStringSaved += new ConnectionStringManager.ConnectionStringSavedHandler(ConnectionStringDialog_ConnectionStringSaved);
		//            ConnectionStringDialog.Visibility = Visibility.Visible;
		//        }
		//    }
		//}

		private DbmlData FindDbmlDataByName(string name) {
			DbmlData dbml = null;

			foreach (DbmlData dbmlData in Databases.Items) {
				if (dbmlData.Name == name) {
					dbml = dbmlData;
					break;
				}
			}

			return dbml;
		}

		private void Sync(object sender, RoutedEventArgs e) {
			try {
				Button button = sender as Button;
				string dbName = button.CommandParameter.ToString();

				_selectedDbml = FindDbmlDataByName(dbName);
				bool connectionStringFound = false;

				if (_selectedDbml != null && _selectedDbml.DB != null) {
					if (_selectedDbml.DB.Connection != null) {
						_selectedDbml.DB.Connection.ConnectionString = GetConnectionString(_selectedDbml.DB.Connection.SettingsPropertyName);

						if (!string.IsNullOrEmpty(_selectedDbml.DB.Connection.ConnectionString)) {
							// If we have a connection string, go straight to the SyncTables user control
							UserControl nextUserControl = new SyncTables(_parent, _selectedDbml);
							_parent.LoadUserControl(nextUserControl);
							connectionStringFound = true;
						}
					}

					if (!connectionStringFound) {
						// No connection string means we need to define one - popup the ConnectionStringManager dialog
						ConnectionStringDialog.DbmlData = _selectedDbml;
						ConnectionStringDialog.ConnectionStringSaved += new ConnectionStringManager.ConnectionStringSavedHandler(ConnectionStringDialog_ConnectionStringSaved);
						ConnectionStringDialog.Visibility = Visibility.Visible;
					}
				}
			}
			catch (Exception ex) {
				LogUtil.EventLogMessage(ex.Message, ex.InnerException, ex.StackTrace); 
			}
		}

		// TODO: Is this method and the above the same except for the nextUserControl?  If so, refactor.
		private void Add(object sender, RoutedEventArgs e) {
			try {
				Button button = sender as Button;
				string dbName = button.CommandParameter.ToString();

				_selectedDbml = FindDbmlDataByName(dbName);
				bool connectionStringFound = false;

				if (_selectedDbml != null && _selectedDbml.DB != null) {
					if (_selectedDbml.DB.Connection != null) {
						_selectedDbml.DB.Connection.ConnectionString = GetConnectionString(_selectedDbml.DB.Connection.SettingsPropertyName);
						
						if (!string.IsNullOrEmpty(_selectedDbml.DB.Connection.ConnectionString)) {
							// If we have a connection string, go straight to the DB Manager user control
							UserControl nextUserControl = new DBManager(_parent, _selectedDbml);
							_parent.LoadUserControl(nextUserControl);
							connectionStringFound = true;
						}
					}

					if (!connectionStringFound) {
						// No connection string means we need to define one - popup the ConnectionStringManager dialog
						ConnectionStringDialog.DbmlData = _selectedDbml;
						ConnectionStringDialog.ConnectionStringSaved += new ConnectionStringManager.ConnectionStringSavedHandler(ConnectionStringDialog_ConnectionStringSaved);
						ConnectionStringDialog.Visibility = Visibility.Visible;
					}
				}
			}
			catch (Exception ex) {
				LogUtil.EventLogMessage(ex.Message, ex.InnerException, ex.StackTrace); 
			}
		}

		private string GetConnectionString(string keyName) {
			string connectionString = string.Empty;

			if (_selectedDbml.DB != null && _selectedDbml.DB.Connection != null) {
				// Look for the connection string directly in the dbml
				//if (!string.IsNullOrEmpty(_selectedDbml.DB.Connection.ConnectionString)) {
				//    connectionString = _selectedDbml.DB.Connection.ConnectionString;
				//}
				//else {
					// Next, check for connection string in web.config connectionstrings element
					Project webProject = SolutionUtil.GetWebProject(_parent.DTE);
					string webProjectFolder = FileUtil.GetFolderPath(AddInUtil.GetProjectFilePath(webProject));
					string webConfigFilePath = System.IO.Path.Combine(webProjectFolder, "web.config");
					XElement webConfig = XElement.Load(webConfigFilePath);
					// Find the connectionStrings element
					XElement connectionStrings = webConfig.Elements("connectionStrings").Any() ? webConfig.Element("connectionStrings") : null;
					// TODO: Alter to check for "configSource" attribute on connectionStrings element exactly like is done for appSettings and "file" attribute below
					if (connectionStrings != null) {
						XElement webConfigConnection = (from connString in connectionStrings.Elements("add")
														where connString.Attribute("name").Value == keyName
														select connString).SingleOrDefault();
						if (webConfigConnection != null) {
							connectionString = webConfigConnection.Attributes("connectionString").Any() ? (string)webConfigConnection.Attribute("connectionString") : null;
						}
					}
					if (string.IsNullOrEmpty(connectionString)) {
						// Start looking in web.config appSettings next
						XElement appSettings = webConfig.Elements("appSettings").Any() ? webConfig.Element("appSettings") : null;
						
						// Check to see if web.config references an external appSettings file
						connectionString = GetConnectionStringFromExternalAppSettingsFile(appSettings, keyName);

						if (string.IsNullOrEmpty(connectionString) && appSettings != null) {
							// Next, check for connection string in web.config appSettings element
							connectionString = GetConnectionStringFromAppSettingsElement(appSettings, keyName);
						}
						if (string.IsNullOrEmpty(connectionString) && appSettings != null) {
							// Start looking in machine.config next (usually - C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG)
							string machineConfigFilePath = System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile;
							
							// Check for existence of Framework64 path
							string machineConfigFilePath64 = machineConfigFilePath.Replace("Framework", "Framework64");
							if (File.Exists(machineConfigFilePath64)) {
								// A 64 bit machine.config exists, so check to see if the webproject is using IIS (IIS runs in 64bit on 64bit machines)
								bool usingIIS = Convert.ToBoolean(webProject.Properties.Item("WebApplication.UseIIS").Value);
								if (usingIIS) {
									// The app will use the 64bit machine.config
									machineConfigFilePath = machineConfigFilePath64;
								}
							}


							XElement machineConfigBody = XElement.Load(machineConfigFilePath);
							XElement machineAppSettings = machineConfigBody.Elements("appSettings").Any() ? machineConfigBody.Element("appSettings") : null;

							// Check to see if machine.config references an external appSettings file
							connectionString = GetConnectionStringFromExternalAppSettingsFile(machineAppSettings, keyName);

							if (string.IsNullOrEmpty(connectionString) && machineAppSettings != null) {
								// Next, check for connection string in machine.config appSettings element
								connectionString = GetConnectionStringFromAppSettingsElement(machineAppSettings, keyName);
							}
						}

					}
				//}
			}

			return connectionString;
		}

		private string GetConnectionStringFromAppSettingsElement(XElement appSettings, string keyName) {
			string connectionString = string.Empty;

			XElement appSettingConnection = (from appSetting in appSettings.Elements("add")
											 where appSetting.Attribute("key").Value == keyName
											 select appSetting).SingleOrDefault();
			if (appSettingConnection != null) {
				connectionString = appSettingConnection.Attributes("value").Any() ? (string)appSettingConnection.Attribute("value") : null;
			}

			return connectionString;
		}

		private string GetConnectionStringFromExternalAppSettingsFile(XElement appSettings, string keyName) {
			string connectionString = string.Empty;
			
			if (appSettings != null && appSettings.Attributes("file").Any()) {
				string appSettingsFilePath = (string)appSettings.Attribute("file");
				XElement externalAppSettingsBody = XElement.Load(appSettingsFilePath);
				if (externalAppSettingsBody != null) {
					connectionString = GetConnectionStringFromAppSettingsElement(externalAppSettingsBody, keyName);
				}
			}

			return connectionString;
		}

		private void Remove(object sender, RoutedEventArgs e) {
			try {
				Button button = sender as Button;
				string dbName = button.CommandParameter.ToString();

				_selectedDbml = FindDbmlDataByName(dbName);

				if (_selectedDbml != null) {
					// We need to have a connection element and at least one Table or View or Proc
					if (_selectedDbml.DB != null && _selectedDbml.DB.Connection != null && !string.IsNullOrEmpty(_selectedDbml.DB.Connection.ConnectionString)
						&& (_selectedDbml.DB.Tables.Count > 0 || _selectedDbml.DB.Views.Count > 0 || _selectedDbml.DB.Functions.Count > 0)) {
						// If we have a connection string, go straight to the SyncTables user control
						UserControl nextUserControl = new RemoveTables(_parent, _selectedDbml);
						_parent.LoadUserControl(nextUserControl);
					}
					else {
						MessageBox.Show("Cannot call Remove on dbml without connection string and at least one table - don't allow this button to be seen in these scenarios");
						//LogUtil.EventLogMessage("");
					}
				}
			}
			catch (Exception ex) {
				LogUtil.EventLogMessage(ex.Message, ex.InnerException, ex.StackTrace); 
			}
			// Temp test
			//TransformT4Template(@"e:\Development\Prototype\T4Generator\T4Generator\Templates\TestOne.dnt");
		}

		protected void ConnectionStringDialog_ConnectionStringSaved(object sender, dotNails.CodeGen.Models.DataBase dbmlDatabase) {
			// This will overwrite everything in the dbml.
			if (_selectedDbml != null && dbmlDatabase != null) {
				_selectedDbml.DB = dbmlDatabase;
			}

			// Save the connection string to the web.config file
			Project webProject = SolutionUtil.GetWebProject(_parent.DTE);
			string webProjectFolder = FileUtil.GetFolderPath(AddInUtil.GetProjectFilePath(webProject));
			string webConfigFilePath = System.IO.Path.Combine(webProjectFolder, "web.config");
			XElement webConfig = XElement.Load(webConfigFilePath);
			// Find the connectionStrings element
			XElement connectionStrings = webConfig.Element("connectionStrings");
			XElement existingConnectionString = null;
			//<add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>
			if (connectionStrings != null) {
				existingConnectionString = (from connectionString in connectionStrings.Elements("add")
											where connectionString.Attribute("name").Value == _selectedDbml.DB.Connection.SettingsPropertyName
											select connectionString).SingleOrDefault();

				// Only save if there isn't already a connection string with this name in the web.config
				if (existingConnectionString == null) {
					connectionStrings.Add(new XElement("add",
											new XAttribute("name", dbmlDatabase.Connection.SettingsPropertyName),
											new XAttribute("connectionString", dbmlDatabase.Connection.ConnectionString),
											new XAttribute("providerName", "System.Data.SqlClient")
										));

					webConfig.Save(webConfigFilePath);
				}
			}

			
			
		}

	}
}
